django model operator

Model

ORM

当和数据库通信时,会由于开发语言和数据库的数据格式不一致,导致无法直接通信,使得通信变得很繁琐

ORM是解决此问题的必要方案。

ORM – Object Relational Mapping 对象关系映射

  • python对象 关系表 搭建映射
  • 对象属性 表中的列 搭建映射
  • 使得对象和关系表可以直接通信,极大简化通信过程

Django的Model章节,也不例外的践行了ORM,为model层的开发提供可极大便利

Django中的ORM的搭建,核心是model类,所以Model章节的内容,将围绕model类展开

回顾:Model层开发过程

  • 安装数据库驱动:pip install mysqlclient

  • 设置数据库连接参数(settings.py)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    DATABASES = {
    'default': {
    'ENGINE': 'django.db.backends.mysql',
    'NAME': "db_create",
    "USER": "root",
    "PASSWORD": "123",
    "PORT": "3306",
    "HOST": "localhost"
    }
    }
    ##settings.py中的mysql数据库的设置
  • 安装app;设置开发模式;设置时区 

  • 定义model类

    1
    2
    3
    4
    class User(models.Model):
    name = models.CharField(max_length=20) # varchar(20)
    age = models.IntegerField() # int
    birth = models.DateTimeField() # datetime
  • 生成移植文件  更好的与不同的数据库之间做数据结构的移植

  • 执行移植文件  生成可执行的移植文件

  • 测试Model操作

model-field

  • AutoField 一般不用直接使用,Model中默认的主键类型

  • 每一个mysql表的建立都会有一个键,作为唯一的标识存在!

    1
    2
    Django默认给每个Model一个主键字段(如果没有自己定义的话)
    id = models.AutoField(primary_key=True)
  • IntegerField 4字节 int

  • BigIntegerField 8字节 bigint

  • SmallIntegerField 2字节 smallint

  • FloatField 底层采用float类型 double ==8字节

  • DecimalField(max_digits=5,decimal_places=2) 底层采用Decimal类型 decimal

    必填:max_digits=5 #共有几位数

    必填:decimal_places=2#其中小数位占几位

  • CharField(max_length=20) varchar(20)

    max_length是必填属性

  • TextField longtext  (不会限制字符个数的大小)

  • BooleanField 接收“True/False” tinyint

  • NullBooleanField 接收“True/False/None” tinyint

  • DateTimeField datetime(一般用来作生日的字段)

  • DateField date

    可选:auto_now=true 可做修改时间记录

    可选:auto_now_add=True 可做首次添加时间

    注意:以上两属性之一指定后,该字段不允许被编辑

  • TimeField time

  • FileField varchar

  • ImageField varchar

  • ForeignKey(to=关系对方的类或类名或‘self’,on_delete=级联选项)==(从表使用ForeignKeye)

  • OneToOneFiled(to=关系对方的类或类名或‘self’,on_delete=级联选项)

  • ManyToManyFiled(to=关系对方的类或类名或‘self’)

field-options

  • null 默认False,不能为空

    1
    name = models.CharField(max_length=20,null=True) #可为空
  • default 定义默认值

    1
    2
    3
    name = models.CharField(max_length=20,default="zhj9")
    age = models.IntegerField(default=18)
    birth = models.DateTimeField(default="2018-12-12")

    默认值不会作用在数据库上,而是django自己的约定,通过Model.save()时,会使用默认值

  • primary_key

    1
    2
    id = models.AutoFiled(primary_key=True) #默认追加,不用自己定义
    id = models.CharField(primary_key=True,max_length=32)#如果需要字符串类型的ID可以自己定义
  • unique 列是否唯一

    1
    name = models.CharField(max_length=20,unique=True)
  • db_column 自定义列名,默认和field同名

    1
    name = models.CharField(max_length=20,db_column="name9")
  • db_index 是否在列上建立索引

    1
    name = models.CharField(max_length=20,db_index=True)
  • blank 默认Flase,用于前端页面的form验证

    1
    note = models.CharField(max_length=20,blank=True)

model-meta

  • db_table 设置表名

  • ordering 设置默认的排序规则( Model.objects.all()等查询时的排序规则 )

  • unique_together 设置联合唯一约束

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class MyModel(models.Model):
    salary = models.DecimalField(...)
    .....
    age = models.xxx
    name = models.xxx
    class Meta://unique_together #表示的是元组
    unique_together = (("salary", "salary2"),("age","age2","age3"))
    db_table="my_user"
    ordering=('-age','name'#根据age降序排列,如果age相同,再按照name升序排列

Model-API 之 增删改

1
2
3
4
5
user = User(name="zhj",age=18,birth=datetime.now())
user.save()
或者
user = User.objects.create(name="zzz",age=19,birth=datetime.now()) #创建对象并保存数据,一步完成
##注意,save()没有返回值,create()返回保存的对象
1
2
User.objects.get(pk=3).delete()
User.objects.all().delete()
1
2
3
user = User.objects.get(pk=3)
user.name="new_name"
user.save()

Model-API 之 查询

Manager + QuerySet

每个Model类都会有一个属性:objects,存储一个Manager对象,用来支持查询功能。

Model类中的objects属性,是父类ModelBase生成的。

Manager中有一个方法属性,存储的是QuerySet

查询功能的相关方法是由QuerySet提供实现

User.objects==Manager()

User.objects.all()

1>User(Model(ModelBase)):再User中会由ModelBase为其指派一个属性:objects=Manager()

2>User.objects 获取的User的Manager(),Manager()中有一个属性存储了一个QuerySet

3>User.objects.all() 调用Manager的all()方法

4>Manager会去调用QuerySet的相关方法,完成all的查询

查询方法

如下所有查询方法都是QuerySet实现的,只不过每个Model中都会有一个Manager,Manager接受到如下方法的调用时,会去调用QuerySet的方法,最终实现数据查询。

所以QuerySet这个类 是查询动作的核心支撑

Manager的任务是管理QuerySet,即,在需要的时候去调用QuerySet的对应方法

  • all() - QuerySet

    1
    2
    User.objects.all() #返回QuerySet,其中是所有User的数据
    #<QuerySet [<User: zhj1 19 2018/12/12 00:00:00>, <User: zhj2 20 2018/12/12 00:00:00>,...]>
  • get() - 对象

    1
    2
    3
    4
    #id=1
    User.objects.get(pk=1) #返回查到的数据,没有数据或数据多于1条 报错
    #name='zhj' 而且 age=18的数据
    User.objects.get(name="zhj",age=18)#返回查到的数据,没有数据或数据多于1条 报错
  • filter() - QuerySet

    1
    2
    3
    #id=1
    User.objects.filter(pk=1) #返回一个QuerySet,其中是满足条件的数据
    User.objects.filter(name='zhj',age=18) #可以有多条数据,如果没有数据,就返回一个空白的QuerySet
  • exclude() -QuerySet

    1
    2
    3
    User.objects.exclude(pk=1) #和filter相反,取不满足条件的数据,返回QuerySet
    User.objects.exclude(gender=True)# True/False 或者 1/0 和bool的filed比较
    #no #所有被选择出来的数据都是以列表的数据结构形式返回;
  • first() - 对象

    1
    User.objects.first() #获取QuerySet中的第一个元素,返回一个对象或None
  • last() - 对象

    1
    User.objects.last() #获取QuerySet中的最后一个元素,返回一个对象或None
  • exists() - Boolean

    1
    User.objects.exists() #User对应的表中是否有数据,判断数据表是否为空。返回True/False
  • count()

    1
    2
    3
    User.objects.count() #数据数量
    User.objects.all().count() #数据数量 和上面等价
    User.objects.filter(age=18).count()
  • QuerySet使用补充

    1
    list(User.objects.all())  #转成list
    1
    2
    3
    for e in User.objects.all(): #可以直接遍历
    print(e.age)#e=User对象
    print(e.name)
    1
    2
    if User.objects.filter(name="zhj"):#直接用于判断,QuerySet中没有数据时作为False使用
    print("至少有一个User数据")

QuerySet限制操作

all() , filter() , exclude() , order_by() 都会返回QuerySet对象

firt(),last()只能获取QuerySet对象中的头、尾元素

1
2
3
4
5
6
7
8
9
User.objects.all()[0] #返回QuerySet中第一个对象
User.objects.filter(pk=1,name="zhj")[2] #返回QuerySet中第3个对象

uname="zhj" #可以使用变量在查询中
User.objects.filter(pk=1,name=uname)[2] #返回QuerySet中第3个对象

User.objects.exclude(pk=1,name="zhj")[1:3] #返回包含第[2,4)个对象(前闭后开)的新的QuerySet (子集)
User.objects.exclude(pk=1,name="zhj")[:3] # 等价于[0:3]
User.objects.exclude(pk=1,name="zhj")[2:] # 返回第3个到最后一个对象的新的QuerySet

排序

  • order_by

    1
    2
    3
    4
    5
    6
    7
    User.objects.all().order_by("pk") #根据id升序,返回QuerySet
    User.objects.all().order_by("age")
    User.objects.all().order_by("age","name")
    User.objects.all().order_by("age","-name") #根据age升序,age相同的根据name倒序

    a = "age" #使用变量
    User.objects.all().order_by(a,"name")

条件查询

在众多查询方法中适合做条件查询的有:get()、filter()、exclude()

get(pk=1,name=”zhj”) 如此只是等值查询,远不能满足查询需求

  • 比较

    不支持 “> < >= <= “这些符号,替换为关键字 lt、gt、lte、gte

    1
    2
    3
    4
    5
    6
    7
    8
    9
    User.objects.filter(pk__lt=10) #id小于10
    User.objects.get(pk__gt=100,age__lte=18) #id大于100,age小于等于18 (两个放在一起写:就是and 且)
    #日期比较也可以
    (exclude除了满足条件之外所有)
    User.objects.exclude(birth__gte="2018-5-22 12:12:12") #yyyy-mm-dd HH:MM:SS格式
    User.objects.filter(birth__gt=datetime.now())

    dd = "2018-5-22 12:12:12"#使用变量
    User.objects.exclude(birth__gte=dd) #使用变量
  • 模糊查询(所有带有i的表示大小写不敏感,即可以大小写通吃)

    • exact、iexact (了解)

    • contains、icontains

    • startswith、istartswith

    • endswith、iendswith

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      User.objects.filter(name__exact="ss") # name等于ss
      User.objects.filter(name__iexact="ss") # 数据库本身大小写不敏感,所以 等价于exact
      User.objects.filter(name="ss") #和上面等价

      User.objects.filter(name__contains="s") #name中包含"s"
      User.objects.filter(name__icontains="s") #name中包含"s"或"S"

      User.objects.filter(name__startswith="s")
      User.objects.filter(name__istartswith="s")# name是以"s"或"S"开始

      User.objects.filter(name__endswith="s")
      User.objects.filter(name__iendswith="s")# name是以"s"或"S"结尾
  • 范围

    • in()  ==一个可迭代对象

    • range()  ==range(len(length)) 一个连续的整数范围内

      1
      2
      3
      4
      5
      User.objects.filter(name__in=("zhj","zz")) # where name in("zhj","zz")
      User.objects.filter(age__in=(18,19,22))

      User.objects.filter(age__range=(18,20)) # where age between 18 and 20 [18,20]
      User.objects.filter(birth__range=('2018-05-10','2018-06-14'))
  • 日期

    • year

    • month

    • day 每月的第几天

    • hour

    • minute

    • second

    • week 一年的第几周 1-52 or 53

    • week_day 周几 周日=1 周六=7

      1
      2
      3
      User.objects.filter(birth__year="2018")
      User.objects.filter(birth__month="1") # 生日是1月 可选值1-12
      User.objects.filter(birth__week_day__gt=1) # 生日日期大于周日
  • 空值:(用来挑选某个字段是否为空值)

    • isnull         

      1
      User.objects.filter(age__isnull=True)

映射查询

select * from t_user;查询全部列

select id,name from t_user;查询部分列–映射查询

  • values()(两种方法:1.改变返回值的内部数据结构,2.查询部分列)每个元素都是一个dict对象

    (每行都是一个dict,每个元素都是一个dict)

  • only()

1
2
3
4
5
6
7
8
9
10
11
#values()方法的第一种用法:改变返回值的内部结构
User.objects.all().values() #返回所有数据的QuerySet,其中每行数据都是一个dict而不再是Model对象
User.objects.values() #返回所有数据的QuerySet,其中每行数据都是一个dict而不再是Model对象
User.objects.filter(age__gt=18).values() #返回age>18的所有数据的QuerySet,其中每行数据都是一个dict
#values()方法的第二种用法:查询部分列(返回QuerySet,其中每个元素都是一个dict)
User.objects.all.values("pk") #select id from t_user;
User.objects.values("pk") #等价上面写法:select id from t_user;
User.objects.filter(age__lt=20).values("name") # select name from t_user where age<20
User.objects.filter(name__contains="zhj").values("pk","age")#select id,age,name from
#t_user where name like "%zhj%"
这些queryset方法的底层实现还都是:cursor.execute(sql)
1
2
3
4
5
#返回<QuerySet [<User>, <User>] >
users = User.objects.only("pk","name") #会率先查询id和name,其他列暂时不查
#即,QuerySet内的User中暂时只有id和name有值
users[0].age #用到其他列数据时,才会查询它
#此处涉及到延缓查询的特性:lazy-load

聚合-aggregate (返回的是一行数据)

  • Avg() (返回的是一个字典类型的对象)
  • Count() (默认的值为:字段名_函数名 : 值)
  • Max() key
  • Min()
  • Sum()
1
2
3
4
5
from django.db.models import Count, Max,Avg,Min,Sum
#返回 {'pk__max': 4, 'name__min': 'zhj'}
User.objects.aggregate(Max("pk"),Min('name'))#selecet max(id),min(name) from t_user;
#可以定义别名,影响返回值结构 -- 返回 {'mm': 4, 'name__min': 'zhj'}
User.objects.aggregate(mm = Max("pk"),Min('name'))#selecet max(id) as mm,min(name) from t_user;

分组-annotate

#select 分组条件列,聚合1,聚合2 from 表 group by 分组条件列

Models.objects.values(‘分组条件列’).annotate(聚合1,聚合2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#每种年龄中的用户的最大id和用户的最小生日
#select age,max(id),min(birth) from t_user group by age
User.objects.values("age").annotate(Max('pk'),Min('birth'))

#大于18的每种年龄中的用户的最大id和用户的最小生日
#加where : select age,max(id),min(birth) from t_user where age>18 group by age
User.objects.values("age").filter(age__gt=18).annotate(Max('pk'),Min('birth'))
#也可以写成这样:
User.objects.filter(age__gt=18).values("age").annotate(mm=Max('pk'),mi=Min("birth"))

having的存在一般用来:对分组的再次条件判断 == filter()的条件查询
#加having : select age,max(id),min(birth) from t_user group by age having max(id)>3
User.objects.values("age").annotate(Min('birth') ,p = Max('id')).filter(p__gt=3)

#加别名
User.objects.values("age").filter(age__gt=18).annotate(mm=Max('pk'),Min('birth'))
#加排序:如果涉及到排序,先给字段起个别名,再来排序
User.objects.values("age").filter(age__gt=18).annotate(mm=Max('pk'),Min('birth')).order_by('mm')

#补充:
#每个用户的最大id,最小生日 (等价于根据id分组),这些都是唯一的?
User.objects.annotate(mm=Max('pk'),mi=Min('birth'))

分组:annotate==一次用:filter()==having

​ 聚合函数max(),min(),avg(),count(),sum()

​ values() == group by的一次综合使用

​ mm == as 起别名

QuerySet.query ==返回QuerySet执行的sql语句

User.objects.filter(…).query//可以看到查询语句

F对象和Q对象

User.objects.filter(id__gt=1) #id大于1

问题:查询id大于age的用户? 尝试:User.objects.filter(id__gt=age)–错误

当查询条件中需要另外的列时,可以使用F

User.objects.filter(id__lt=F(‘age’))–正确

1
2
3
4
User.objects.filter(id__lt=F('age')) #id小于age
User.objects.filter(id__lt=F('age')+1) #id小于age+1
User.objects.filter(id__lt=F('age')*10/2) #id小于age*10/2
User.objects.filter(id__lt=F('age')+F('salary')) #id小于age+salary

User.objects.filter(id__lt=1,name__contains=”zhj”) #id小于1 而且 name包含“zhj”

问题:查询id小于1 或 name含有“zhj”的数据?

​ 查询name不含有“zhj”的数据?

当需要 “或 (|) 非(~)” 逻辑时,可以使用Q

1
2
3
4
5
6
7
User.objects.filter(Q(id__gt=3)|Q(id__lt=2)) #id大于3 或 id小于2
User.objects.filter(Q(id__gt=3)|~Q(name__contains="zhj")) #id大于3 或 name不含有“zhj”
User.objects.filter(Q(id__lt=3)|Q(age__gt=F("id")*10)) #id小于3 或 age大于id*10

User.objects.filter(Q(id__gt=3)|Q(name__contains="zhj"),age__lt=2) #id大于3 或 name包含"zhj" 且 age小于2

User.objects.filter(Q(id__gt=3)|Q(name__contains="zhj"),~Q(age__lt=2) #id大于3 或 name包含"zhj" 且 age不小于2

Raw-SQL

如果以上的支持不能满足需求,django也支持直接执行sql语句

1
2
3
4
5
6
7
8
9
#返回RawQuerySet,其中是一个个的Model对象
User.objects.raw("select id,name from first_app_user3 where id=1")
User.objects.raw("select id,name from first_app_user3 where age=%s and name=%s",[18,"zhj9"])

#注意,不要在外部自己拼接sql
cc = "select id,name from first_app_user3 where age=%d and name='%s'"%[18,"zhj9"]
User.objects.raw(cc)
#如上会有sql注入风险:
cc = "select id,name from first_app_user3 where age=%d and name='%s'"%(18,"' or '1'='1")

django的QuerySet和Raw-SQL都已经防止了sql注入

Model

关联关系

数据库的表之间都是有联系的,正如我们之前讲的,有关联关系的存在

关联关系的种类:1对1 , 1对多 , 多对多

在Model中体现关联关系,进而更好的在多表之间操作数据

  • ForeignKey(to=关系对方的类或类名或‘self’,on_delete=级联选项)
  • OneToOneFiled(to=关系对方的类或类名或‘self’,on_delete=级联选项)
  • ManyToManyFiled(to=关系对方的类或类名或‘self’)

级联选项 on_delete=级联删除 

  • CASCADE 级联删除
  • SET_NULL 外键置空(如果允许空的话),前提是外键是允许为空的
  • PROTECT 不允许直接删除主表
  • SET_DEFAULT 需要为外键列设置默认值,默认值也应该是合法的外键值,可以在表中预留一个id  (SET==SET_DEFAULT)
  • SET 将外键设置为某个值,值应该是合法的外键值,可以在表中预留一个id
  • DO_NOTHING django什么也不做,由数据库决定是否合法

查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
11关系: Person:没有关系属性   Passport:有关系属性person
#单独查一方
per = Person.objects.all()
pass = Passport.objects.all()

pass[0].per #通过关系属性名 获取关系对方
per[0].passport #没有关系属性时,通过关系对方全小写的类名,获得对方
(per[0].passport这种方法只适用于一对一的表单)
pass[0].person.name #继续获取属性值
per[0].passport.note #继续获取属性值

#1对1关系:关联查询,会进行表连接
#注意:在查询条件中,如在filter中 passport__note是passport的note字段
passport__id是passport的id字段
person__name是person的name字段,
#护照的note中包含"a"的Person,会做表连接
Person.objects.filter(passport__note__contains="a")
Person.objects.filter(passport__note="a") ##护照的note是"a"的Person,会做表连接
Person.objects.filter(passport__id=1)#护照的id是1的Person,会做表连接
Person.objects.filter(passport=1)#护照的id是1的Person,会做表连接(和上一个等价,只有id可以如此)

Passport.objects.filter(person=1)#id为1的person的Passport
Passport.objects.filter(person__id=1)#id为1的person的Passport
Passport.objects.filter(person__id__gt=1)#id大于1的person的Passport
Passport.objects.filter(person__name__contains="z")#名字中含有'z'的person的Passport
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1对多关系   Category(1):没有关系属性   Goods(*):其中有关系属性cate9
#单独查一方
cs = Category.objects.all()
gs = Goods.objects.all()

cs[0].goods_set # 没有多方的关系属性,通过"多方类名_set"获得多方
# 此时只是返回一个RelatedManger,不支持遍历和限制获取子集 而且并没有数据,需要如下
cs[0].goods_set.filter(gprice__gt=200) #第一个类别中价格大于200的所有商品 (如此才有值)
cs[0].goods_set.all() # 第一个类别中的所有商品(如此才有值)

gs[0].cate9 #获得第一个商品的类别对象
gs[0].cate9.ctitle #获得对象后 自然可以继续获取属性

#关联查询:会进行表连接
cate9是Goods字段中的一个列,查询的结果为该字段属性的所有值
Goods.objects.filter(cate9__gte=1) # 类别id大于等于1的商品
Goods.objects.filter(cate9__pk__gte=1) # 类别id大于等于1的商品
Goods.objects.filter(cate9__id__exact=1)#类别id等于1的商品
Goods.objects.filter(cate9__title__contains="s")# 列名标题含有"s"的商品

Category.objects.filter(goods__gt=1)#id大于1的商品的类别
Category.objects.filter(goods__title__contains="a")#标题中含有"a"的商品的类别

#注意在进行联合查询时,可能会由重复数据出现,解决:
list(set(Category.objects.filter(goods__price__gt=200)))
// 因为在不同类型中有多个价格大于200的产品
#它的返回值是商品的类,所以它不同的price会对应多个相同的类,所以去重,加list
#转换类型,是为了更好的遍历数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
多对多关系   Course:有对方关系属性stus   Student:没有对方关系属性 
#查询一方
stus = Student.objects.all()              
cours = Course.objects.all()             

stus[0].course_set #返回ManyRelatedManager,但并没有数据
stus[0].course_set.all() #获得对方数据
stus[0].course_set.filter(title__contains='s') #获得第一个学生的 标题中含有"s"的课程

cours[0].students.all()#选择了第一个课程的所有学生
cours[0].students.filter(age__gt=18)#选择了第一个课程的所有年龄大于18岁的学生

#关联查询
Student.objects.filter(course__title__contains="h") #标题中含有"h"的课程中的学生
Course.objects.filter(students__name="zzz") #姓名为zzz的学生参加的课程
Course.objects.filter(students__age__gt=18) #年龄大于18的学生参加的课程

增加

单独增加主表方(没有外键一方) 和单表增加无差异

1
Category(title="男装",...).save()

为已存在的主表方附加从表数据(常见的增加情形)

1
2
3
4
5
6
c = Category.objects.get(pk=1)
c.goods_set.create(title="zzz6",price=100) #不需要再save

c = Category.objects.get(pk=1)
g=Goods(title="zzz6",price=100,cate9=c)#为商品的关系属性赋值
g.save() #需要save

同时增加主从数据

1
2
3
4
5
6
7
8
9
10
11
12
g = Goods(title="zzz6",price=100)
c = Category(title="类别1",note="xx")
g.save() #此时,外键的值为null
c.save()
c.goods_set.add(g) #会同步数据库,补充外键值



c = Category(title="类别1",note="xx")
c.save()
g = Goods(title="zzz6",price=100,cate9=c)
g.save() #此时,good是有外键值得

删除

单独删除从表方,和删除单表无差异

1
Goods.objects.get(pk=1).delete()

删除主表方,此时要看从表方的级联设置,会影响到从表方

(在所有的有关联的表中,如果直接删除从表,则是简单的删除即可,如果要是删除主表,则从表也会跟着从表类的外联属性设置值,而变化)

1:1 支持CASCADE SET_NULL(前提:外键列允许为null)

1:* 都支持

*:* 不支持级联删除

(如果是OneToOneField可以用on_delete=”CASCADE”,也可用SET_NULL,null=true)null的默认值必须是为空的

(on_delete:级联属性,当删除主表行时,从表的对应行的关系)(属性设置)

(CASCADE:对应的从表行也会随着删除,一般考虑到数据的宝贵性,建议不使用它)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#如果要删除主,所有从的外键置null  (重要)
per = models.OneToOneField(to=Person,on_delete=models.SET_NULL,null=True)
#如果要删除主,所有从一起删除
per = models.OneToOneField(to=Person,on_delete=models.CASCADE,null=True)
#如果要删除主,django什么也不做,有数据库决定是否合法
per = models.OneToOneField(to=Person,on_delete=models.DO_NOTHING,null=True)
#如果要删除主,所有从的外键置为6 (重要)
(SET(6),设置的对应默认值,一般是主表存在,专用于被删除的列)
cate115 = models.ForeignKey(to="Category",on_delete=models.SET(6))
#如果要删除主,所有从的外键置为默认值5 (重要)(默认的值不一定要求主已经存在的)
(设置的默认值必须是有一个models.SET(),设置的外键值是主键已经存在的)
cate115 = models.ForeignKey(to="Category",on_delete=models.SET_DEFALUT,default="5")
#如果要删除主,如果有从数据存在,不允许删除
cate115 = models.ForeignKey(to="Category",on_delete=models.PROTECT)

修改

查询出数据,修改属性,然后save()即可

1
2
3
4
5
6
7
8
cs = Category.objects.all()
c = cs[0]
c.title="new_title"
c.save()

坑:
Category.objects.all()[0].title="new_title" #查询一次,并修改
Category.objects.all()[0].save() #查询一次,并保存

QuerySet 高级特性:懒加载 Lazy-load

1
2
3
4
5
a = Person.objects.all()
b = Category.objects.all()
print(a)
#如上代码只执行了一次
所谓的QuerySet的懒加载:就是当数据在需要的使用的时候,它才会去加载,否则会一直不执行filter,all,(返回的是一个QuerySet对象)get(pk=id)返回的是object对象

测试方式

找到mysql的安装目录下的my.ini文件,添加配置:

在cmd下net stop mysql

​ net start mysql

在搜索框中输入fuwu

1
2
3
4
5
6
7
> [mysqld]
> .....
> log = "E:/mysql_log.sql" #设置日志文件,记录sql语句执行
> 或
> general-log=1
> general_log_file=E:/mysql_log.sql
>